import crawler.*;
import database.DataBase;
import video.Video;
import downloader.DownloaderThread;
import downloader.Part;
import downloader.Resource;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.*;
import java.util.concurrent.CyclicBarrier;

public class Main {
    private static final String savePath = "download";

    /**
     * Acquire target urls from database, then crawl pertinent information and
     * store them in database.
     */
    private static void crawl() throws Exception {
        ArrayList<String> targetURLS = DataBase.getURLS();
        Crawler[] crawlers = new Crawler[5];

        // Every time crawl five urls.
        for (int i = 0; i < targetURLS.size(); i += 5) {
            if (i % 5 == 0 && i != 0) {
                for (Crawler c : crawlers) {
                    c.join();
                }
            }
            crawlers[i % 5] = new Crawler(targetURLS.get(i));
            crawlers[i % 5].start();
        }
    }

    /**
     * Download the video with multi-threads.
     */
    private static void download(Resource resource) throws Exception {
        String fileName = resource.getFileName();
        int threadNum = resource.getThreadNum();
        CyclicBarrier barrier = new CyclicBarrier(threadNum + 1);
        int fileSize = resource.getSize();
        System.out.println(fileName + ": " + fileSize + " bytes.");
        int partLength = fileSize % threadNum == 0 ? fileSize / threadNum
                : fileSize / threadNum + 1;

        File file = resource.getFile();
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        raf.setLength(fileSize);
        raf.close();

        for (int j = 0; j < threadNum; ++j) {
            Part part = new Part(j * partLength, partLength - 1);
            resource.addPart(part);
            new DownloaderThread(part, file, resource.getUrl(), barrier).start();
        }

        barrier.await();
        System.out.println(fileName + " has been downloaded.");
    }

    /**
     * Extract the file extension from the download link.
     *
     * @param url download link
     * @return ".flv" or ".mp4"
     */
    private static String extractFileName(String url) {
        int begin = url.lastIndexOf(".");
        int end = url.indexOf("?");
        return url.substring(begin, end);
    }

    public static void main(String[] args) {
        // Crawl videos.
        try {
            crawl();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Find out all top videos from the database.
        HashSet<Video> topVideos = null;
        try {
            topVideos = DataBase.getTopVideos();
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        // Download these videos.
        for (Video v : topVideos) {
            int aid = v.getAid();
            try {
                String url = Crawler.getDownloadLink(aid);
                String fileName = aid + extractFileName(url);
                Resource resource = new Resource(url, savePath, fileName, 5);
                download(resource);
            } catch (Exception e2) {
                System.out.println("catch: " + aid);
                e2.printStackTrace();
            } finally {
                System.out.println("---------------------------------------------");
            }
        }
    }
}
